#include "httprequest.h"

using namespace std;

const unordered_set<string> HttpRequest::DEFAULT_HTML {     // 预定义的一组默认HTML资源路径集合
    "/index", "/register", "/login",
    "/welcome", "/video", "/picture",
};

const unordered_map<string, int> HttpRequest::DEFAULT_HTML_TAG {    // HTML资源到标签的映射
    {"./register.html", 0}, {"./login.html", 1},
};

// 重置所有成员变量至初始状态，清空请求头和POST数据映射
void HttpRequest::Init() {
    method_ = path_ = version_ = body_ = "";
    state_ = REQUEST_LINE;
    header_.clear();    // 清除请求头map
    post_.clear();
}

// 检查HTTP头部的 "Connection" 字段来决定客户端是否请求了持久连接
bool HttpRequest::IsKeepAlive() const {
    if(header_.count("Connection") == 1) {
        // 查找header的map中的connction状态，并且判断该connection所对应的
        // 值是否为keep_alive,并检查http协议版本
        return header_.find("Connection")->second == "keep-alive" && version_ == "1.1";

    }
    return false;
}

/*
GET /picture HTTP/1.0
Host: 212.129.223.138:1316
Connection: close
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*\/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://yubo.asia/
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
*/
/*
parse() 方法循环读取Buffer中的HTTP请求内容，根据请求的当前状态(state_)调用不同的解析方法：
    ParseRequestLine_() 解析请求行（方法、路径、版本）。
    ParseHeader_() 解析请求头，并在遇到非标准格式时切换到BODY状态。
    ParseBody_() 处理请求体内容。
*/
bool HttpRequest::parse(Buffer& buff) {
    const char CRLF[] = "\r\n";
    if(buff.ReadableBytes() <= 0) {     // 前提是buffer有数据可读
        return false;
    }

    while(buff.ReadableBytes() && state_ != FINISH) {
        // 返回第一次匹配点的指针，获取一行数据，根据\r\n 为结束标志
        // std::search，它用于在一个序列中搜索另一个序列的首次出现
        const char* lineEnd = search(buff.Peek(), buff.BeginWriteConst(), CRLF, CRLF + 2);
        std::string line(buff.Peek(), lineEnd);
        switch (state_)
        {
        case REQUEST_LINE:
            if(!ParseRequestLine_(line)) {
                return false;
            }
            ParsePath_();
            break;
        case HEADERS:
            ParseHeader_(line);
            if(buff.ReadableBytes() <= 2) {
                state_ = FINISH;
            }
            break;
        case BODY:
            ParseBody_(line);
            break;
        default:
            break;
        }
        if(lineEnd == buff.BeginWrite()) {break;}   // 说明已经读完了
        buff.RetriveUntil(lineEnd + 2);             // 跳过回车换行符"\r\n"
    }
    // 记录当前请求行
    LOG_DEBUG("[%s], [%s], [%s]", method_.c_str(), path_.c_str(), version_.c_str());
    return true;
}

// 检查请求路径，如果路径是 / 或与预定义的默认HTML资源相匹配，则为其添加 .html 扩展名
void HttpRequest::ParsePath_() {
    if(path_ == "/") {
        path_ = "/index.html";
    } else {
        for(auto &item : DEFAULT_HTML) {
            if(item == path_) {
                path_ += ".html";
                break;
            }
        }
    }
}

// 使用正则表达式解析请求行
// GET /picture HTTP/1.0
bool HttpRequest::ParseRequestLine_(const string& line) {
    regex pattern("^([^ ]*) ([^ ]*) HTTP/([^ ]*)$");
    smatch subMatch;
    if(regex_match(line, subMatch, pattern)) {
        method_ = subMatch[1];
        path_ = subMatch[2];
        version_ = subMatch[3];
        state_ = HEADERS;   // 更改状态
        return true;
    }
    LOG_ERROR("RequestLine Error!");
    return false;
}

// 使用正则表达式将请求头键值对加入到 header_ 映射中
/*
Host: 212.129.223.138:1316
Connection: close
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*\/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://yubo.asia/
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
*/
void HttpRequest::ParseHeader_(const string& line) {
    regex pattern("^([^:]*): ?(.*)$");
    smatch subMatch;
    if(regex_match(line, subMatch, pattern)) {
        header_[subMatch[1]] = subMatch[2];     // 创建相应的键值对
    } else {
        state_ = BODY;      // 转移状态
    }
}

// 将请求体内容保存下来，并调用 ParsePost_() 解析POST数据
void HttpRequest::ParseBody_(const string& line) {
    body_ = line;
    ParsePost_();
    state_ = FINISH;
    LOG_DEBUG("Body:%s, len:%d", line.c_str(), line.size());
}

int HttpRequest::ConverHex(char ch) {
    if(ch >= 'A' && ch <= 'F') return ch -'A' + 10;
    if(ch >= 'a' && ch <= 'f') return ch -'a' + 10;
    return ch;
}

// 解析POST数据
void HttpRequest::ParsePost_() {
    if(method_ == "POST" && header_["Content-Type"] == "application/x-www-form-urlencoded") {
        ParseFromUrlencoded_();
        if(DEFAULT_HTML_TAG.count(path_)) {
            int tag = DEFAULT_HTML_TAG.find(path_)->second; //传递路径对应的值
            LOG_DEBUG("Tag:%d", tag);
            if(tag == 0 || tag == 1) {
                bool isLogin = (tag == 1);
                if(UserVerify(post_["username"], post_["password"], isLogin)) {
                    path_ = "/welcome.html"; //路径跳转
                } else {
                    path_ = "/error.html";  //验证失败
                }
            }
        }
    }
}

// 对POST数据进行URL解码，并将键值对存入 post_ 映射中
void HttpRequest::ParseFromUrlencoded_() {
    if(body_.size() == 0) {return;}

    string key, value;
    int num = 0;
    int n = body_.size();
    int i = 0, j = 0;

    for(; i < n; i++) {
        char ch = body_[i];
        switch(ch)
        {
            case '=':
                key = body_.substr(j, i - j);
                j = i + 1;
                break;
            case '+':
                body_[i] = ' ';
                break;
            case '%':
                num = ConverHex(body_[i + 1]) * 16 + ConverHex(body_[i + 2]);
                body_[i + 2] = num % 10 + '0';
                body_[i + 1] = num / 10 + '0';
                i += 2;
                break;
            case '&':
                value = body_.substr(j, i - j);
                j = i + 1;
                post_[key] = value;
                LOG_DEBUG("%s = %s", key.c_str(), value.c_str());
                break;
            default:
                break;  
        }
    }
    assert(j <= i);
    if(post_.count(key) == 0 && j < i) {
        value = body_.substr(j, i - j);
        post_[key] = value;
    }
}

// 验证用户名和密码，如果是登录操作则比较密码，否则执行注册操作，涉及数据库查询与操作
bool HttpRequest::UserVerify(const string& name, const string &pwd, bool isLogin) {
    if(name == "" || pwd == "") {return false;}
    LOG_INFO("Verify name:%s pwd:%s", name.c_str(), pwd.c_str());
    MYSQL* sql;
    SqlConnRAII(&sql, SqlConnPool::Instance());     // 自动释放连接,连接池已经初始化
    assert(sql);

    bool flag = false;
    unsigned int j = 0;
    char order[256] = {0};
    MYSQL_FIELD *fields = nullptr;  // 保存字段信息
    MYSQL_RES* res = nullptr;       // 保存结果信息

    if(!isLogin) {flag = true; }
    //SQL查询语句,查询用户名与密码
    snprintf(order, 256, "SELECT username, password FROM user WHERE username='%s' LIMIT 1", name.c_str());
    LOG_DEBUG("%s", order);

    if(mysql_query(sql, order)) {
        mysql_free_result(res);
        return false;
    }
    res = mysql_store_result(sql);      // 保存结果
    j = mysql_num_fields(res);          // 获取结果中的字段数量
    fields = mysql_fetch_field(res);    // 获取字段信息

    while(MYSQL_ROW row = mysql_fetch_row(res)) {
        LOG_DEBUG("MYSQL ROW: %s %s", row[0], row[1]);
        string password{row[1]};  //保存查询到的密码
        if(isLogin) {   //当前行为是登录行为
            if(pwd == password) {
                flag = true;
            } else {
                flag = false;
                LOG_ERROR("pwd error!");
            }
        } else {
            flag = false;
            LOG_DEBUG("user used!");
        }
    }
    mysql_free_result(res);

    if(!isLogin && flag == true) {
        LOG_DEBUG("register!");  // 说明正在注册
        bzero(order, 256);       // 清除

        //  格式化的SQL插入语句
        snprintf(order, 256, "INSERT INTO user(username, password) VALUES('%s','%s')", name.c_str(), pwd.c_str());
        LOG_DEBUG("%s", order);
        if(mysql_query(sql, order)) {
            LOG_DEBUG("Insert error!");
            flag = false;
        }
        flag = true;
    }
    SqlConnPool::Instance()->FreeConn(sql);
    LOG_DEBUG("UserVerify success!!");
    return flag;
}

std::string HttpRequest::path() const {
    return path_;
}

std::string& HttpRequest::path() {
    return path_;
}

std::string HttpRequest::method() const {
    return method_;
}

std::string HttpRequest::version() const {
    return version_;
}

// 拿到post中map所存储的对应键的值,从已解析的POST数据中检索指定键的值
std::string HttpRequest::GetPost(const std::string& key) const {
    assert(key != "");
    if(post_.count(key) == 1) {
        return post_.find(key)->second;
    }
    return "";
}

std::string HttpRequest::GetPost(const char* key) const {
    assert(key != nullptr);
    if(post_.count(key) == 1) {
        return post_.find(key)->second;
    }
    return "";
}